/*
 * Copyright (C) 2013 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY SCE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebTileManagerProxy.h"

#if ENABLE(MANX_HTMLTILE)

#include "UpdateInfo.h"
#include "WebContext.h"
#include "WebPageGroup.h"
#include "WebPreferences.h"
#include "WebTileManagerMessages.h"
#include "WebTileManagerProxyMessages.h"
#include "WebTileProxy.h"

#include <WebCore/HTMLTile.h>

namespace WebKit {

const char* WebTileManagerProxy::supplementName()
{
    return "WebTileManagerProxy";
}

PassRefPtr<WebTileManagerProxy> WebTileManagerProxy::create(WebContext* context)
{
    return adoptRef(new WebTileManagerProxy(context));
}

WebTileManagerProxy::WebTileManagerProxy(WebContext* context)
    : WebContextSupplement(context)
    , m_webTilesPageGroup(WebPageGroup::create("WebTilePageGroup"))
{
    WebContextSupplement::context()->addMessageReceiver(Messages::WebTileManagerProxy::messageReceiverName(), this);

    // Disable AC and WebGL, not supported by HTMLTile.
    // For safety, we also disable everything not explicitely required for HTMLTile
    WebPreferences* tilePreferences = m_webTilesPageGroup->preferences();

#define DISABLE_BOOL_PREFERENCE(KeyUpper, KeyLower, TypeName, Type, DefaultValue) if (DefaultValue) tilePreferences->set##KeyUpper(false);

FOR_EACH_WEBKIT_BOOL_PREFERENCE(DISABLE_BOOL_PREFERENCE)

#undef DISABLE_BOOL_PREFERENCE

    // these are explicitely allowed, see BUG 32374
    tilePreferences->setJavaScriptEnabled(true);
    tilePreferences->setJavaScriptMarkupEnabled(true);
    tilePreferences->setRequestAnimationFrameEnabled(true);
    tilePreferences->setLoadsImagesAutomatically(true);
    tilePreferences->setAuthorAndUserStylesEnabled(true);
    tilePreferences->setXSSAuditorEnabled(true);
    tilePreferences->setWebSecurityEnabled(true);

    tilePreferences->setCookieEnabled(true);
    tilePreferences->setLocalStorageEnabled(true);
    tilePreferences->setOfflineWebApplicationCacheEnabled(true);

    // enable for remote web inspector access
    tilePreferences->setDeveloperExtrasEnabled(true);
}

void WebTileManagerProxy::createWebTile(uint64_t tileID, uint32_t width, uint32_t height, uint64_t pageID)
{
    ASSERT(!m_webTiles.contains(tileID));

    RefPtr<WebTileProxy> webTile = WebTileProxy::create(this, m_webTilesPageGroup.get());
    webTile->setTileID(tileID);
    webTile->setTileSize(width, height);
    webTile->setParentPage(WebProcessProxy::webPage(pageID));

    m_webTiles.set(tileID, webTile);

    dispatchTileValid(tileID);
}

void WebTileManagerProxy::destroyWebTile(uint64_t tileID)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.remove(tileID);
}

void WebTileManagerProxy::pauseWebTile(uint64_t tileID)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.get(tileID)->pause();
}

void WebTileManagerProxy::resumeWebTile(uint64_t tileID)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.get(tileID)->resume();
}

void WebTileManagerProxy::sendWebTileEvent(uint64_t tileID, uint32_t eventType)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }

    RefPtr<WebTileProxy> webTile = m_webTiles.get(tileID);

    switch (eventType) {
    case WebCore::HTMLTile::FocusIn: webTile->setFocused(true); break;
    case WebCore::HTMLTile::FocusOut: webTile->setFocused(false); break;
    case WebCore::HTMLTile::ButtonPress: webTile->setClicked(true); break;
    case WebCore::HTMLTile::ButtonRelease: webTile->setClicked(false); break;
    }
}

void WebTileManagerProxy::runJavaScriptInWebTile(uint64_t tileID, const String &jsScript, uint64_t callbackID)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.get(tileID)->evalScript(jsScript, callbackID);
}

void WebTileManagerProxy::didChangeWebTileURL(uint64_t tileID, const String& url)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.get(tileID)->setTileURL(url);
}

void WebTileManagerProxy::didUpdateWebTile(uint64_t tileID)
{
    if (!m_webTiles.contains(tileID)) {
        // TODO log warning
        return;
    }
    m_webTiles.get(tileID)->didUpdate();
}

WebTileManagerProxy::~WebTileManagerProxy()
{
    if (m_webTilesContext)
        m_webTilesContext->enableProcessTermination();
}

WebContext* WebTileManagerProxy::webTilesContext()
{
    if (!m_webTilesContext) {
        m_webTilesContext = WebContext::create(String()); // no injected bundle for the tiles context

        // Replace the parent context's webprocess binary with our own, which is expected to be in the same directory
        const String webProcessDir = context()->webProcessPath().left(context()->webProcessPath().reverseFind('/')+1);
        m_webTilesContext->setWebProcessPath(webProcessDir + "WebProcessHTMLTile.self");

        if (!context()->userStorageDirectory().isEmpty())
            m_webTilesContext->setUserStorageDirectory(context()->userStorageDirectory() + "/htmltile");
        if (!context()->systemStorageDirectory().isEmpty())
            m_webTilesContext->setSystemStorageDirectory(context()->systemStorageDirectory() + "/htmltile");
    }
    return m_webTilesContext.get();
}

void WebTileManagerProxy::contextDestroyed()
{
    m_webTiles.clear();
}

bool WebTileManagerProxy::shouldTerminate(WebProcessProxy*) const
{
    return m_webTiles.isEmpty();
}

void WebTileManagerProxy::refWebContextSupplement()
{
    ref();
}

void WebTileManagerProxy::derefWebContextSupplement()
{
    deref();
}

void WebTileManagerProxy::dispatchTileValid(uint64_t tileID) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);
    tile->parentPage()->process()->send(Messages::WebTileManager::TileValid(tileID), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchTileUpdated(uint64_t tileID, const UpdateInfo &updateInfo) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);
    tile->parentPage()->process()->send(Messages::WebTileManager::TileUpdated(tileID, updateInfo), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchTileUnresponsive(uint64_t tileID) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::TileUnresponsive(tileID), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchTileCrashed(uint64_t tileID) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::TileCrashed(tileID), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchLoadCompleted(uint64_t tileID) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::LoadCompleted(tileID), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchLoadFailed(uint64_t tileID, uint32_t errorCode) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::LoadFailed(tileID, errorCode), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchLocationChangeRequested(uint64_t tileID, const String& url) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::LocationChangeRequested(tileID, url), tile->parentPage()->pageID());
}

void WebTileManagerProxy::dispatchRunJavaScriptResultCallback(uint64_t tileID, uint64_t callbackID, const String& result) const
{
    RefPtr<WebTileProxy> tile = m_webTiles.get(tileID);
    ASSERT(tile);

    tile->parentPage()->process()->send(Messages::WebTileManager::RunJavaScriptResultCallback(tileID, callbackID, result), tile->parentPage()->pageID());
}

} // namespace WebKit

#endif // ENABLE(MANX_HTMLTILE)
